package com.sparrow.rpc.serialize.impl;

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import com.sparrow.rpc.socket.msg.req.RpcLoginRequest;
import com.sparrow.rpc.socket.msg.req.RpcRequest;
import com.sparrow.rpc.socket.msg.resp.RpcResponse;
import com.sparrow.rpc.serialize.Serializer;
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.ObjenesisStd;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 *Protostuff 是一个为 Java 设计的高性能序列化框架，
 * 它基于 Protobuf 的设计思想，但并不依赖 Google 的 Protobuf 实现。
 * Protostuff 支持多种序列化格式，并提供了 对普通 Java POJO 的直接序列化支持，无需编写 .proto 文件。
 * 在不影响性能的前提下简化序列化流程，使开发者可以像使用 JDK 的 Serializable 一样直接操作 POJO。
 */
public class ProtostuffSerializer extends Serializer {


    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();
    static {
        getSchema(RpcRequest.class);
        getSchema(RpcResponse.class);
        getSchema(RpcLoginRequest.class);
    }

    private Objenesis objenesis = new ObjenesisStd(true);

    @SuppressWarnings("unchecked")
    private static <T> Schema<T> getSchema(Class<T> cls) {
        // for thread-safe
        return (Schema<T>) cachedSchema.computeIfAbsent(cls, RuntimeSchema::createFrom);
    }

    public static <T> void addSchema(Class<T> clas) {
        getSchema(clas);
    }

    @Override
    public <T> byte[] serialize(T obj) {
        Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(cls);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    @Override
    public <T> T deserialize(byte[] bytes, Class<T> clazz) {
        try {
            T message = (T) objenesis.newInstance(clazz);
            Schema<T> schema = getSchema(clazz);
            ProtostuffIOUtil.mergeFrom(bytes, message, schema);
            return message;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
}
